"use strict";

exports.__esModule = true;
require("core-js/modules/es.error.cause.js");
var _moment = _interopRequireDefault(require("moment"));
var _pikaday = _interopRequireDefault(require("@handsontable/pikaday"));
var _baseEditor = require("../baseEditor");
var _textEditor = require("../textEditor");
var _element = require("../../helpers/dom/element");
var _object = require("../../helpers/object");
var _unicode = require("../../helpers/unicode");
var _browser = require("../../helpers/browser");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); }
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
const EDITOR_TYPE = exports.EDITOR_TYPE = 'date';
const SHORTCUTS_GROUP_EDITOR = 'dateEditor';
const DEFAULT_DATE_FORMAT = 'DD/MM/YYYY';

/**
 * @private
 * @class DateEditor
 */
var _DateEditor_brand = /*#__PURE__*/new WeakSet();
class DateEditor extends _textEditor.TextEditor {
  constructor() {
    super(...arguments);
    /**
     * Gets the current date format for this cell.
     *
     * @returns {string}
     */
    _classPrivateMethodInitSpec(this, _DateEditor_brand);
    /**
     * @type {boolean}
     */
    _defineProperty(this, "parentDestroyed", false);
    /**
     * @type {Pikaday}
     */
    _defineProperty(this, "$datePicker", null);
  }
  static get EDITOR_TYPE() {
    return EDITOR_TYPE;
  }
  init() {
    if (typeof _moment.default !== 'function') {
      throw new Error('You need to include moment.js to your project.');
    }
    if (typeof _pikaday.default !== 'function') {
      throw new Error('You need to include Pikaday to your project.');
    }
    super.init();
    this.hot.addHook('afterDestroy', () => {
      this.parentDestroyed = true;
      this.destroyElements();
    });
    this.hot.addHook('afterSetTheme', (themeName, firstRun) => {
      if (!firstRun) {
        this.close();
      }
    });
  }

  /**
   * Create data picker instance.
   */
  createElements() {
    super.createElements();
    this.datePicker = this.hot.rootDocument.createElement('DIV');
    this.datePickerStyle = this.datePicker.style;
    this.datePickerStyle.position = 'absolute';
    this.datePickerStyle.top = 0;
    this.datePickerStyle.left = 0;
    this.datePickerStyle.zIndex = 9999;
    this.datePicker.setAttribute('dir', this.hot.isRtl() ? 'rtl' : 'ltr');
    (0, _element.addClass)(this.datePicker, 'htDatepickerHolder');
    this.hot.rootPortalElement.appendChild(this.datePicker);

    /**
     * Prevent recognizing clicking on datepicker as clicking outside of table.
     */
    this.eventManager.addEventListener(this.datePicker, 'mousedown', event => {
      if ((0, _element.hasClass)(event.target, 'pika-day')) {
        this.hideDatepicker();
      }
      event.stopPropagation();
    });
  }

  /**
   * Destroy data picker instance.
   */
  destroyElements() {
    const datePickerParentElement = this.datePicker.parentNode;
    if (this.$datePicker) {
      this.$datePicker.destroy();
    }
    if (datePickerParentElement) {
      datePickerParentElement.removeChild(this.datePicker);
    }
  }

  /**
   * Prepare editor to appear.
   *
   * @param {number} row The visual row index.
   * @param {number} col The visual column index.
   * @param {number|string} prop The column property (passed when datasource is an array of objects).
   * @param {HTMLTableCellElement} td The rendered cell element.
   * @param {*} value The rendered value.
   * @param {object} cellProperties The cell meta object (see {@link Core#getCellMeta}).
   */
  prepare(row, col, prop, td, value, cellProperties) {
    super.prepare(row, col, prop, td, value, cellProperties);
  }

  /**
   * Open editor.
   *
   * @param {Event} [event=null] The event object.
   */
  open() {
    let event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
    const shortcutManager = this.hot.getShortcutManager();
    const editorContext = shortcutManager.getContext('editor');
    this.showDatepicker(event);
    super.open();
    editorContext.addShortcuts([{
      keys: [['ArrowLeft']],
      callback: () => {
        this.$datePicker.adjustDate('subtract', 1);
      }
    }, {
      keys: [['ArrowRight']],
      callback: () => {
        this.$datePicker.adjustDate('add', 1);
      }
    }, {
      keys: [['ArrowUp']],
      callback: () => {
        this.$datePicker.adjustDate('subtract', 7);
      }
    }, {
      keys: [['ArrowDown']],
      callback: () => {
        this.$datePicker.adjustDate('add', 7);
      }
    }], {
      group: SHORTCUTS_GROUP_EDITOR
    });
  }

  /**
   * Close editor.
   */
  close() {
    var _this$$datePicker;
    // If the date picker was never initialized (e.g. during autofill), there's nothing to destroy.
    if ((_this$$datePicker = this.$datePicker) !== null && _this$$datePicker !== void 0 && _this$$datePicker.destroy) {
      this.$datePicker.destroy();
    }
    this.hot._registerTimeout(() => {
      const editorManager = this.hot._getEditorManager();
      editorManager.closeEditor();
      this.hot.view.render();
      editorManager.prepareEditor();
    });
    const shortcutManager = this.hot.getShortcutManager();
    const editorContext = shortcutManager.getContext('editor');
    editorContext.removeShortcutsByGroup(SHORTCUTS_GROUP_EDITOR);
    super.close();
  }

  /**
   * Finishes editing and start saving or restoring process for editing cell or last selected range.
   *
   * @param {boolean} restoreOriginalValue If true, then closes editor without saving value from the editor into a cell.
   * @param {boolean} ctrlDown If true, then saveValue will save editor's value to each cell in the last selected range.
   */
  finishEditing() {
    let restoreOriginalValue = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
    let ctrlDown = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
    super.finishEditing(restoreOriginalValue, ctrlDown);
  }

  /**
   * Show data picker.
   *
   * @param {Event} event The event object.
   */
  showDatepicker(event) {
    const dateFormat = _assertClassBrand(_DateEditor_brand, this, _getDateFormat).call(this);
    const isMouseDown = this.hot.view.isMouseDown();
    const isMeta = event ? (0, _unicode.isFunctionKey)(event.keyCode) : false;
    let dateStr;
    this.datePicker.style.display = 'block';
    this.$datePicker = new _pikaday.default(this.getDatePickerConfig());
    if (typeof this.$datePicker.useMoment === 'function') {
      this.$datePicker.useMoment(_moment.default);
    }
    this.$datePicker._onInputFocus = function () {};
    if (this.originalValue) {
      dateStr = this.originalValue;
      if ((0, _moment.default)(dateStr, dateFormat, true).isValid()) {
        this.$datePicker.setMoment((0, _moment.default)(dateStr, dateFormat), true);
      }

      // workaround for date/time cells - pikaday resets the cell value to 12:00 AM by default, this will overwrite the value.
      if (this.getValue() !== this.originalValue) {
        this.setValue(this.originalValue);
      }
      if (!isMeta && !isMouseDown) {
        this.setValue('');
      }
    } else if (this.cellProperties.defaultDate) {
      dateStr = this.cellProperties.defaultDate;
      if ((0, _moment.default)(dateStr, dateFormat, true).isValid()) {
        this.$datePicker.setMoment((0, _moment.default)(dateStr, dateFormat), true);
      }
      if (!isMeta && !isMouseDown) {
        this.setValue('');
      }
    } else {
      // if a default date is not defined, set a soft-default-date: display the current day and month in the
      // datepicker, but don't fill the editor input
      this.$datePicker.gotoToday();
    }
  }

  /**
   * Hide data picker.
   */
  hideDatepicker() {
    this.datePickerStyle.display = 'none';
    this.$datePicker.hide();
  }

  /**
   * Get date picker options.
   *
   * @returns {object}
   */
  getDatePickerConfig() {
    var _options$format;
    const htInput = this.TEXTAREA;
    const options = {};
    if (this.cellProperties && this.cellProperties.datePickerConfig) {
      (0, _object.deepExtend)(options, this.cellProperties.datePickerConfig);
    }
    const origOnSelect = options.onSelect;
    const origOnClose = options.onClose;
    options.field = htInput;
    options.trigger = htInput;
    options.container = this.datePicker;
    options.bound = false;
    options.keyboardInput = false;
    options.format = (_options$format = options.format) !== null && _options$format !== void 0 ? _options$format : _assertClassBrand(_DateEditor_brand, this, _getDateFormat).call(this);
    options.reposition = options.reposition || false;
    // Set the RTL to `false`. Due to the https://github.com/Pikaday/Pikaday/issues/647 bug, the layout direction
    // of the date picker is controlled by juggling the "dir" attribute of the root date picker element.
    // See line @64 of this file.
    options.isRTL = false;
    options.onSelect = value => {
      let dateStr = value;
      if (!isNaN(dateStr.getTime())) {
        dateStr = (0, _moment.default)(dateStr).format(_assertClassBrand(_DateEditor_brand, this, _getDateFormat).call(this));
      }
      this.setValue(dateStr);
      if (origOnSelect) {
        origOnSelect();
      }
      if ((0, _browser.isMobileBrowser)()) {
        this.hideDatepicker();
      }
    };
    options.onClose = () => {
      if (!this.parentDestroyed) {
        this.finishEditing(false);
      }
      if (origOnClose) {
        origOnClose();
      }
    };
    return options;
  }

  /**
   * Refreshes datepicker's size and position. The method is called internally by Handsontable.
   *
   * @private
   * @param {boolean} force Indicates if the refreshing editor dimensions should be triggered.
   */
  refreshDimensions(force) {
    var _wtOverlays$getParent;
    super.refreshDimensions(force);
    if (this.state !== _baseEditor.EDITOR_STATE.EDITING) {
      return;
    }
    this.TD = this.getEditedCell();
    if (!this.TD) {
      this.hideDatepicker();
      return;
    }
    const {
      rowIndexMapper,
      columnIndexMapper
    } = this.hot;
    const {
      wtOverlays
    } = this.hot.view._wt;
    const {
      wtTable
    } = (_wtOverlays$getParent = wtOverlays.getParentOverlay(this.TD)) !== null && _wtOverlays$getParent !== void 0 ? _wtOverlays$getParent : this.hot.view._wt;
    const firstVisibleRow = rowIndexMapper.getVisualFromRenderableIndex(wtTable.getFirstPartiallyVisibleRow());
    const lastVisibleRow = rowIndexMapper.getVisualFromRenderableIndex(wtTable.getLastPartiallyVisibleRow());
    const firstVisibleColumn = columnIndexMapper.getVisualFromRenderableIndex(wtTable.getFirstPartiallyVisibleColumn());
    const lastVisibleColumn = columnIndexMapper.getVisualFromRenderableIndex(wtTable.getLastPartiallyVisibleColumn());
    if (this.row >= firstVisibleRow && this.row <= lastVisibleRow && this.col >= firstVisibleColumn && this.col <= lastVisibleColumn) {
      const offset = this.TD.getBoundingClientRect();
      this.datePickerStyle.top = `${this.hot.rootWindow.pageYOffset + offset.top + (0, _element.outerHeight)(this.TD)}px`;
      let pickerLeftPosition = this.hot.rootWindow.pageXOffset;
      if (this.hot.isRtl()) {
        pickerLeftPosition += offset.right - (0, _element.outerWidth)(this.datePicker);
      } else {
        pickerLeftPosition += offset.left;
      }
      this.datePickerStyle.left = `${pickerLeftPosition}px`;
    } else {
      this.hideDatepicker();
    }
  }
}
exports.DateEditor = DateEditor;
function _getDateFormat() {
  var _this$cellProperties$;
  return (_this$cellProperties$ = this.cellProperties.dateFormat) !== null && _this$cellProperties$ !== void 0 ? _this$cellProperties$ : DEFAULT_DATE_FORMAT;
}